home *** CD-ROM | disk | FTP | other *** search
- /*
- * fsm.c -- PPP Finite State Machine
- *
- * Copyright 1991-1992 William Allen Simpson
- * Licensed for non-profit non-commercial distribution
- * in Merit's PPP LAP for MacTCP
- *
- * Copyright 1992-1993 Merit Network, Inc. and The Regents of the
- * University of Michigan. Usage of this source code is restricted
- * to non-profit, non-commercial purposes. The source is provided
- * "as-is", without warranty.
- *
- * This code has been derived solely from the Jan 1991 public
- * domain release of PPP included in Phil Karn's KA9Q.
- */
-
- #include "ppp.h"
-
- /* Convert header in host form to network form */
- void htoncnf(struct config_hdr *cnf, struct bufheader *bufptr)
- {
- register char *cp;
-
- /* Prepend bytes for LCP/IPCP header */
- cp = (bufptr->dataptr -= CONFIG_HDR_LEN);
- bufptr->length += CONFIG_HDR_LEN;
-
- /* Load header with proper values */
- *cp++ = cnf->code;
- *cp++ = cnf->id;
- put16(cp, cnf->len);
- }
-
- /* Extract header from incoming packet */
- int
- ntohcnf(struct config_hdr *cnf, struct bufheader *bufptr)
- {
- char cnfb[CONFIG_HDR_LEN];
- register char *ptr = cnfb;
-
- if ( yankbuf( bufptr, ptr, CONFIG_HDR_LEN ) < CONFIG_HDR_LEN )
- return -1;
-
- cnf->code = *ptr++;
- cnf->id = *ptr++;
- cnf->len = get16(ptr);
- return 0;
- }
-
- /* Extract configuration option header */
- int
- ntohopt(struct option_hdr *opt, struct bufheader *bufptr)
- {
- char optb[OPTION_HDR_LEN];
-
- if ( yankbuf( bufptr, optb, OPTION_HDR_LEN ) < OPTION_HDR_LEN )
- return -1;
-
- opt->type = optb[0];
- opt->len = optb[1];
- return 0;
- }
-
- /* Set a timer in case an expected event does not occur */
- void
- fsm_timer(fsm_p)
- struct fsm_s *fsm_p;
- {
- start_timer( &(fsm_p->timer) );
- }
-
- /* Send a packet to the remote host */
- int
- fsm_send(struct fsm_s *fsm_p, b_8 code, b_8 id, struct bufheader *bufptr)
- {
- LapInfo *lap = fsm_p->lap;
- struct config_hdr hdr;
- void *dummy = nil; /* dummy ptr to indicate LCP echo req */
-
- if (bufptr == nil)
- return -1; /* Must pass a bufptr */
-
- switch( hdr.code = code ) {
- case CONFIG_REQ:
- case TERM_REQ:
- case ECHO_REQ:
- /* Save ID field for match against replies from remote host */
- fsm_p->lastid = lap->ppp_id;
- /* fallthru */
- case PROT_REJ:
- case DISCARD_REQ:
- /* Use a unique ID field value */
- hdr.id = lap->ppp_id++;
- break;
-
- default: /* CONFIG_ACK CONFIG_NAK CONFIG_REJ TERM_ACK CODE_REJ ECHO_REPLY */
- /* Use ID sent by remote host */
- hdr.id = id;
- break;
- }
-
- switch( code ) {
- case ECHO_REQ:
- dummy = bufptr; /* special hack for LCP echo requests */
- /* fall thru */
- case DISCARD_REQ:
- makeroom(bufptr, 4); /* make space for magic number */
- /* fall thru */
- case ECHO_REPLY: /* put our magic number here */
- put32(bufptr->dataptr,
- lap->lcp_i.local.work.lcp_option.magic_number);
- };
-
- hdr.len = bufptr->length + CONFIG_HDR_LEN;
-
- /* Prepend header to packet data */
- htoncnf(&hdr,bufptr); /* prepend control protocol header */
- htonppp(lap, fsm_p->pdc.protocol, bufptr); /* prepend ppp header */
- QueueFrame ( lap, bufptr, dummy );
- return 0;
- }
-
- /* Send a configuration request */
- int
- fsm_sendreq(fsm_p)
- struct fsm_s *fsm_p;
- {
- struct bufheader *bufptr;
-
- if ( fsm_p->retry <= 0 )
- return -1;
-
- fsm_p->retry--;
- fsm_timer(fsm_p);
-
- bufptr = makereq(fsm_p);
- return( fsm_send(fsm_p, CONFIG_REQ, 0, bufptr) );
- }
-
- /* Send a termination request */
- static int
- fsm_sendtermreq(fsm_p)
- struct fsm_s *fsm_p;
- {
- if ( fsm_p->retry <= 0 )
- return -1;
-
- fsm_p->retry--;
- fsm_timer(fsm_p);
- return( fsm_send(fsm_p, TERM_REQ, 0, getbuffer()) );
- }
-
- /* Send Terminate Ack */
- static int
- fsm_sendtermack(struct fsm_s *fsm_p, b_8 id)
- {
- return( fsm_send(fsm_p, TERM_ACK, id, getbuffer()) );
- }
-
- /* This layer is down */
- void
- fsm_tld(struct fsm_s *fsm_p)
- {
- LapInfo *lap = fsm_p->lap;
-
- if (fsm_p->pdc.fsmi == Lcp) {
- fsm_down( &(lap->ppp_fsm[IPcp]) ); /* IPCP down */
- pap_down( &(lap->ppp_fsm[Pap]) ); /* PAP down */
- }
- }
-
- /* This layer is finished */
- void
- fsm_tlf(struct fsm_s *fsm_p)
- {
- stop_timer(&(fsm_p->timer)); /* stop the restart timer */
- if (fsm_p->pdc.fsmi == Lcp)
- link_close(fsm_p->lap); /* can close the link now */
- else /* IPcp */
- fsm_close( &(fsm_p->lap->ppp_fsm[Lcp]) ); /* close LCP */
- }
-
- /* This layer up, Configuration negotiation complete */
- void
- fsm_tlu(struct fsm_s *fsm_p)
- {
- LapInfo *lap = fsm_p->lap;
- struct proto_s *proto_p = fsm_p->pdv;
-
- stop_timer(&(fsm_p->timer));
- fsm_p->state = fsmOPENED;
-
- if (fsm_p->pdc.fsmi == Lcp) {
-
- /* Set Max Transmission Unit for outgoing packets */
- lap->outMaxPacketSize = proto_p->remote.work.lcp_option.mru;
- if ( (proto_p->remote.want_negotiate & LCP_N_MRU) &&
- lap->outMaxPacketSize > proto_p->remote.want.lcp_option.mru)
- lap->outMaxPacketSize = proto_p->remote.want.lcp_option.mru;
-
- /* set up async ctl maps */
- lap->PPP_RecvACM = proto_p->local.work.lcp_option.accm;
- lap->PPP_XmitACM = proto_p->remote.work.lcp_option.accm;
-
- /* check for authentication */
- lap->ppp_phase = pppAUTHENTICATE;
- lap->ppp_flags &= ~PPP_AP_REMOTE;
-
- if (proto_p->remote.work_negotiate & LCP_N_AUTHENT) {
- if (proto_p->remote.work.lcp_option.authentication == PPP_PAP_PROTOCOL)
- pap_remote(lap);
- }
- /* re-check for authentication */
- ppp_ready(lap);
- } else { /* IPcp */
- int rslots = 0;
- int tslots = 0;
-
- if (proto_p->local.work_negotiate & IPCP_N_COMPRESS)
- rslots = proto_p->local.work.ipcp_option.slots;
- if (proto_p->remote.work_negotiate & IPCP_N_COMPRESS) {
- tslots = proto_p->remote.work.ipcp_option.slots;
- if (tslots > IPCP_SLOT_HI)
- tslots = IPCP_SLOT_HI;
- }
- if ( rslots != 0 || tslots != 0 )
- slhc_init( lap, rslots, tslots );
- }
- }
-
- /* This layer start */
- void
- fsm_tls(struct fsm_s *fsm_p)
- {
- LapInfo *lap = fsm_p->lap;
-
- if (fsm_p->pdc.fsmi == Lcp) {
- if (lap->ppp_phase == pppDEAD)
- link_open(lap); /* need to open link before we can start */
- else
- lap->ppp_phase = pppESTABLISH; /* we are in establish phase */
- } else /* IPcp */
- fsm_open( &(lap->ppp_fsm[Lcp]) ); /* start LCP */
- }
-
- /************************************************************************/
- /* E V E N T P R O C E S S I N G */
- /************************************************************************/
-
- /* Process incoming packet */
- void
- fsm_proc(struct fsm_s *fsm_p, struct bufheader *bufptr)
- {
- struct config_hdr hdr;
- int result;
-
- switch(fsm_p->state) {
- case fsmINITIAL:
- case fsmSTARTING:
- release(bufptr);
- return; /* not ready to receive packets yet */
- }
-
- if ( ntohcnf(&hdr, bufptr) == -1 ) {
- PPP_DEBUG_CHECKS("\pshort config packet" );
- release(bufptr);
- return;
- }
-
- hdr.len -= CONFIG_HDR_LEN; /* Length includes envelope */
-
- switch(hdr.code) {
- case CONFIG_REQ:
- switch(fsm_p->state) {
- case fsmOPENED: /* Unexpected event? */
- fsm_tld(fsm_p);
- fsm_sendreq(fsm_p); /* send a new config request */
- fsm_p->state =
- (proc_request(fsm_p, &hdr, bufptr) == 0)
- ? fsmACK_Sent : fsmREQ_Sent;
- break;
-
- case fsmSTOPPED:
- fsm_reset(fsm_p); /* init restart counter and reset option vars */
- fsm_sendreq(fsm_p);
- /* fallthru */
- case fsmREQ_Sent:
- case fsmACK_Sent: /* Unexpected event? */
- fsm_p->state =
- (proc_request(fsm_p, &hdr, bufptr) == 0)
- ? fsmACK_Sent : fsmREQ_Sent;
- break;
-
- case fsmACK_Rcvd:
- if (proc_request(fsm_p, &hdr, bufptr) == 0) {
- fsm_tlu(fsm_p);
- } else {
- /* give peer time to respond */
- fsm_timer(fsm_p);
- }
- break;
-
- case fsmCLOSED:
- /* Don't accept any connections */
- fsm_sendtermack(fsm_p, hdr.id);
- /* We are attempting to close connection; */
- /* wait for timeout to resend a Terminate Request */
- /* fall thru */
- default: /* fsmCLOSING, fsmSTOPPING */
- release(bufptr);
- };
- break;
-
- case CONFIG_ACK:
- switch(fsm_p->state) {
- case fsmREQ_Sent:
- case fsmACK_Sent:
- if (proc_ack(fsm_p, &hdr, bufptr) == 0) {
- fsm_p->retry = fsm_p->pdc.try_req; /* initial restart counter */
- if (fsm_p->state == fsmREQ_Sent)
- fsm_p->state = fsmACK_Rcvd;
- else
- fsm_tlu(fsm_p);
- }
- break;
-
- case fsmOPENED: /* Crossed connection? */
- fsm_tld(fsm_p);
- /* fallthru */
- case fsmACK_Rcvd: /* Crossed connection? */
- release(bufptr);
- fsm_sendreq(fsm_p);
- fsm_p->state = fsmREQ_Sent;
- break;
-
- case fsmCLOSED:
- case fsmSTOPPED:
- /* Out of Sync; kill the remote */
- fsm_sendtermack(fsm_p, hdr.id);
- /* fallthru */
- default: /* fsmCLOSING, fsmSTOPPING */
- /* We are attempting to close connection; */
- /* wait for timeout to resend a Terminate Request */
- release(bufptr);
- };
- break;
-
- case CONFIG_NAK:
- case CONFIG_REJ:
-
- switch(fsm_p->state) {
- case fsmREQ_Sent:
- case fsmACK_Sent:
- fsm_p->retry = fsm_p->pdc.try_req; /* initial restart counter */
- /* Update our config request to reflect NAK/REJed options */
- if (hdr.code == CONFIG_NAK)
- result = proc_nak(fsm_p, &hdr, bufptr);
- else
- result = proc_reject(fsm_p, &hdr, bufptr);
- if (result == 0)
- /* Send updated config request */
- fsm_sendreq(fsm_p);
- break;
-
- case fsmOPENED: /* Crossed connection? */
- fsm_tld(fsm_p);
- /* fallthru */
- case fsmACK_Rcvd: /* Crossed connection? */
- release(bufptr);
- fsm_sendreq(fsm_p);
- fsm_p->state = fsmREQ_Sent;
- break;
-
- case fsmCLOSED:
- case fsmSTOPPED:
- /* Out of Sync; kill the remote */
- fsm_sendtermack(fsm_p, hdr.id);
- /* fallthru */
- default: /* fsmCLOSING, fsmSTOPPING */
- /* We are attempting to close connection; */
- /* wait for timeout to resend a Terminate Request */
- release(bufptr);
- };
- break;
-
- case TERM_REQ:
- release(bufptr); /* release buffer */
- switch(fsm_p->state) {
- case fsmOPENED:
- fsm_p->state=fsmSTOPPING;
- fsm_tld(fsm_p);
- fsm_sendtermack(fsm_p, hdr.id);
- fsm_p->retry = 0; /* zero restart counter */
- fsm_timer(fsm_p); /* restart the timer */
- break;
-
- case fsmACK_Rcvd:
- case fsmACK_Sent:
- fsm_p->state = fsmREQ_Sent;
- /* fallthru */
- default: /* REQ_Sent, CLOSED, STOPPED, CLOSING, STOPPING */
- /* Unexpected, but make them happy */
- fsm_sendtermack(fsm_p, hdr.id);
- };
- break;
-
- case TERM_ACK:
- release(bufptr); /* release buffer */
- switch(fsm_p->state) {
- case fsmCLOSING:
- fsm_p->state = fsmCLOSED;
- fsm_tlf(fsm_p);
- break;
- case fsmSTOPPING:
- fsm_p->state = fsmSTOPPED;
- fsm_tlf(fsm_p);
- break;
-
- case fsmOPENED:
- /* Remote host has abruptly closed connection */
- fsm_tld(fsm_p);
- fsm_sendreq(fsm_p);
- fsm_p->state = fsmREQ_Sent;
- break;
-
- case fsmACK_Rcvd:
- fsm_p->state = fsmREQ_Sent;
-
- }
- break;
-
- case CODE_REJ:
- case PROT_REJ: /* should add some code to see what was rejected */
- release(bufptr);
- if (fsm_p->state == fsmACK_Rcvd)
- fsm_p->state = fsmREQ_Sent;
- break;
-
- case ECHO_REQ:
- if (fsm_p->state == fsmOPENED) /* should maybe check for looped request */
- fsm_send( fsm_p, ECHO_REPLY, hdr.id, bufptr );
- else
- release(bufptr); /* ignore */
- break;
-
- case ECHO_REPLY:
- fsm_p->lap->echo_count = 0; /* reset echo counter */
- /* fall thru */
- case DISCARD_REQ:
- release(bufptr);
- break;
-
- default:
- PPP_DEBUG_CHECKS("\pUnrecognized option type");
- hdr.len += CONFIG_HDR_LEN; /* restore length */
- htoncnf( &hdr, bufptr ); /* put header back on */
- fsm_send( fsm_p, CODE_REJ, hdr.id, bufptr );
-
- if (fsm_p->state == fsmOPENED) {
- fsm_tld(fsm_p);
- fsm_sendreq(fsm_p);
- fsm_p->state = fsmREQ_Sent;
- }
- }
- }
-
- /* Timeout while waiting for reply from remote host */
- static void
- fsm_timeout(void)
- {
- struct TMtimer *timerp;
- struct fsm_s *fsm_p;
-
- asm {
- move.l a1,timerp
- }
- fsm_p = timerp->fsm_p;
-
- switch(fsm_p->state) {
- case fsmREQ_Sent:
- case fsmACK_Rcvd:
- case fsmACK_Sent:
- if (fsm_p->retry > 0) {
- fsm_sendreq(fsm_p);
- fsm_p->state = (fsm_p->state == fsmACK_Sent ? fsmACK_Sent : fsmREQ_Sent);
- } else {
- fsm_p->state = fsmSTOPPED;
- fsm_tlf(fsm_p);
- }
- break;
-
- case fsmCLOSING:
- case fsmSTOPPING:
- if (fsm_p->retry > 0) {
- fsm_sendtermreq(fsm_p);
- } else {
- fsm_p->state = (fsm_p->state == fsmCLOSING ? fsmCLOSED : fsmSTOPPED);
- fsm_tlf(fsm_p);
- }
- break;
- }
- }
-
- /************************************************************************/
- /* A d m i n i s t r a t i v e E v e n t s */
- /************************************************************************/
-
- /* Up Event */
- void
- fsm_up(fsm_p)
- struct fsm_s *fsm_p;
- {
- switch ( fsm_p->state ) {
- case fsmSTARTING:
- fsm_reset(fsm_p); /* reset option values and the restart counter */
- fsm_sendreq(fsm_p);
- fsm_p->state = fsmREQ_Sent;
- break;
- case fsmINITIAL:
- fsm_p->state = fsmCLOSED;
- };
- }
-
- /* Down Event */
- void
- fsm_down(fsm_p)
- struct fsm_s *fsm_p;
- {
- switch ( fsm_p->state ) {
- case fsmREQ_Sent:
- case fsmACK_Rcvd:
- case fsmACK_Sent:
- case fsmSTOPPING:
- stop_timer(&(fsm_p->timer));
- fsm_p->state = fsmSTARTING;
- break;
-
- case fsmOPENED:
- fsm_p->state = fsmSTARTING;
- fsm_tld(fsm_p);
- break;
-
- case fsmSTOPPED:
- fsm_p->state = fsmSTARTING;
- fsm_tls(fsm_p);
- break;
-
- case fsmCLOSING:
- stop_timer(&(fsm_p->timer));
- case fsmCLOSED:
- fsm_p->state = fsmINITIAL;
- }
- }
-
- /* Open Event */
- void
- fsm_open(fsm_p)
- struct fsm_s *fsm_p;
- {
- switch ( fsm_p->state ) {
- case fsmINITIAL:
- fsm_p->state = fsmSTARTING; /* we are starting */
- fsm_tls(fsm_p);
- break;
- case fsmCLOSED:
- fsm_reset(fsm_p); /* reset options and init restart counter */
- fsm_sendreq(fsm_p);
- fsm_p->state = fsmREQ_Sent;
- break;
- case fsmCLOSING:
- fsm_p->state = fsmSTOPPING;
- /* fallthru */
- case fsmSTOPPED:
- case fsmSTOPPING:
- fsm_down(fsm_p); /* Implement the restart option */
- fsm_up(fsm_p);
- };
- }
-
- /* Close Event */
- void
- fsm_close(fsm_p)
- struct fsm_s *fsm_p;
- {
- switch ( fsm_p->state ) {
- case fsmOPENED:
- fsm_tld(fsm_p);
- /* fallthru */
- case fsmACK_Sent:
- case fsmREQ_Sent:
- case fsmACK_Rcvd:
- fsm_p->retry = fsm_p->pdc.try_terminate;
- fsm_sendtermreq(fsm_p);
- /* fallthru */
- case fsmSTOPPING:
- fsm_p->state = fsmCLOSING;
- break;
-
- case fsmSTOPPED:
- fsm_p->state = fsmCLOSED;
- break;
-
- case fsmSTARTING:
- fsm_p->state = fsmINITIAL;
- break;
-
- /* case fsmCLOSED: nothing to do */
- };
- }
-
- /* Initialize the fsm for this protocol
- * Called from protocol _init
- */
- void
- fsm_init(struct fsm_s *fsm_p)
- {
- fsm_p->state = fsmINITIAL; /* initialize state */
- }
-
- /* Reset the option variables, and retry counters */
- static void
- fsm_reset(struct fsm_s *fsm_p)
- {
- LapInfo *lap = fsm_p->lap;
- struct proto_s *proto_p = fsm_p->pdv;
-
- if (fsm_p->pdc.fsmi == Lcp) {
- BlockMove(&(lap->configdata.lcpconf.local), &(proto_p->local),
- sizeof( struct lcp_will_want ));
- BlockMove(&(lap->configdata.lcpconf.remote), &(proto_p->remote),
- sizeof( struct lcp_will_want ));
- BlockMove(lap->lcp_default_p, &(proto_p->remote.work.lcp_option),
- sizeof( struct lcp_value_s ));
- lap->PPP_RecvACM = lap->PPP_XmitACM = LCP_ACCM_DEFAULT;
- fsm_p->retry = lap->configdata.lcpconf.req_tries; /* reset retry counter (config reqs.) */
- fsm_p->pdc.timeout = lap->configdata.lcpconf.timeout;
- } else { /* IPcp */
- BlockMove(&(lap->configdata.ipcpconf.local), &(proto_p->local),
- sizeof( struct ipcp_will_want ));
- BlockMove(&(lap->configdata.ipcpconf.remote), &(proto_p->remote),
- sizeof( struct ipcp_will_want ));
- BlockMove(lap->ipcp_default_p, &(proto_p->remote.work.ipcp_option),
- sizeof( struct ipcp_value_s ));
- fsm_p->retry = lap->configdata.ipcpconf.req_tries; /* reset retry counter (config reqs.) */
- fsm_p->pdc.timeout = lap->configdata.ipcpconf.timeout;
- }
- BlockMove(&(proto_p->local.want), &(proto_p->local.work),
- sizeof (union value_s ));
- proto_p->local.work_negotiate = proto_p->local.want_negotiate;
- proto_p->remote.work_negotiate = FALSE;
- fsm_p->pdc.try_req = fsm_p->retry;
- fsm_p->retry_nak = fsm_p->pdc.try_nak; /* reset the try nak counter */
- /* Initialize timer */
- set_timer(&(fsm_p->timer), fsm_p, (void *) fsm_timeout);
- }